#include "wx/wxprec.h"
#include "wx/window.h"
#include "wx/dnd.h"
#include "wx/tooltip.h"
#include "wx/caret.h"
#include "wx/fontutil.h"
#include "wx/sysopt.h"

#ifdef __WXDEBUG__
#include "wx/thread.h"
#endif

#include <ctype.h>
#include <gtk/gtkversion.h>
#if defined(GTK_DISABLE_DEPRECATED) && GTK_CHECK_VERSION(2,3,0)
#undef GTK_DISABLE_DEPRECATED
#include <gtk/gtkcombo.h>
#define GTK_DISABLE_DEPRECATED
#endif

#define USE_STYLE_SET_CALLBACK 1

#include "wx/gtk/private.h"
#include "wx/gtk/win_gtk.h"
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>

#if !GTK_CHECK_VERSION(2,10,0)
#define GDK_META_MASK 0
#endif

extern bool g_blockEventsOnDrag;
extern bool g_blockEventsOnScroll;
extern wxCursor g_globalCursor;
static wxWindow *g_captureWindow = ( wxWindow* ) NULL;
static bool g_captureWindowHasMouse = false;
wxWindow  *g_focusWindow = ( wxWindow* ) NULL;
wxWindow  *g_focusWindowPending = ( wxWindow* ) NULL;
wxWindow *g_focusWindowLast = ( wxWindow* ) NULL;
wxWindow *g_delayedFocus = ( wxWindow* ) NULL;
GdkEvent *g_lastMouseEvent = ( GdkEvent* ) NULL;
int g_lastButtonNumber = 0;

extern bool g_mainThreadLocked;


#ifdef __WXDEBUG__

#if wxUSE_THREADS
#   define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
#else
#   define DEBUG_MAIN_THREAD
#endif
#else
#define DEBUG_MAIN_THREAD
#endif

#define TRACE_FOCUS _T("focus")

void
gdk_window_warp_pointer( GdkWindow *window, gint x, gint y ) {
  if( !window ) {
    window = gdk_get_default_root_window();
  }
  if( !GDK_WINDOW_DESTROYED( window ) ) {
    XWarpPointer( GDK_WINDOW_XDISPLAY( window ),None,GDK_WINDOW_XID( window ),0, 0, 0, 0,x, y );
  }
}

wxWindow *wxFindFocusedChild( wxWindow *win ) {
  wxWindow* winFocus = g_focusWindow;
  if( !winFocus ) {
    return ( wxWindow * )NULL;
  }
  if( winFocus == win ) {
    return ( wxWindow * )win;
  }
  for( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
       node;
       node = node->GetNext() ) {
    wxWindow *child = wxFindFocusedChild( node->GetData() );
    if( child ) {
      return child;
    }
  }
  return ( wxWindow * )NULL;
}

static void GetScrollbarWidth( GtkWidget* widget, int& w, int& h ) {
  GtkScrolledWindow* scroll_window = GTK_SCROLLED_WINDOW( widget );
  GtkScrolledWindowClass* scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS( scroll_window ) );
  GtkRequisition scroll_req;
  w = 0;
  if( scroll_window->vscrollbar_visible ) {
    scroll_req.width = 2;
    scroll_req.height = 2;
    ( * GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS( scroll_window->vscrollbar ) )->size_request )
    ( scroll_window->vscrollbar, &scroll_req );
    w = scroll_req.width +
        scroll_class->scrollbar_spacing;
  }
  h = 0;
  if( scroll_window->hscrollbar_visible ) {
    scroll_req.width = 2;
    scroll_req.height = 2;
    ( * GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS( scroll_window->hscrollbar ) )->size_request )
    ( scroll_window->hscrollbar, &scroll_req );
    h = scroll_req.height +
        scroll_class->scrollbar_spacing;
  }
}

static void draw_frame( GtkWidget *widget, wxWindow *win ) {
  if( !win->m_hasVMT ) {
    return;
  }
  int dx = 0;
  int dy = 0;
  if( GTK_WIDGET_NO_WINDOW( widget ) ) {
    dx += widget->allocation.x;
    dy += widget->allocation.y;
  }
  int x = dx;
  int y = dy;
  int dw = 0;
  int dh = 0;
  if( win->m_hasScrolling ) {
    GetScrollbarWidth( widget, dw, dh );
    if( win->GetLayoutDirection() == wxLayout_RightToLeft ) {
      x += dw;
    }
  }
  int w = widget->allocation.width - dw;
  int h = widget->allocation.height - dh;
  if( win->HasFlag( wxRAISED_BORDER ) ) {
    gtk_paint_shadow( widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, NULL, NULL, x, y, w, h );
    return;
  }
  if( win->HasFlag( wxSUNKEN_BORDER ) ) {
    gtk_paint_shadow( widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, NULL, NULL, x, y, w, h );
    return;
  }
  if( win->HasFlag( wxSIMPLE_BORDER ) ) {
    GdkGC *gc;
    gc = gdk_gc_new( widget->window );
    gdk_gc_set_foreground( gc, &widget->style->black );
    gdk_draw_rectangle( widget->window, gc, FALSE, x, y, w - 1, h - 1 );
    g_object_unref( gc );
    return;
  }
}

extern "C" {
  static gboolean
  gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindow *win ) {
    if( gdk_event->count == 0 ) {
      draw_frame( widget, win );
    }
    return false;
  }
}

extern "C" {
  void wxgtk_window_size_request_callback( GtkWidget *widget,
      GtkRequisition *requisition,
      wxWindow *win ) {
    int w, h;
    win->GetSize( &w, &h );
    if( w < 2 ) {
      w = 2;
    }
    if( h < 2 ) {
      h = 2;
    }
    requisition->height = h;
    requisition->width = w;
  }
}

extern "C" {
  static
  void wxgtk_combo_size_request_callback( GtkWidget *widget, GtkRequisition *requisition, wxComboBox *win ) {
    int w, h;
    win->GetSize( &w, &h );
    if( w < 2 ) {
      w = 2;
    }
    if( h < 2 ) {
      h = 2;
    }
    GtkCombo *gcombo = GTK_COMBO( win->m_widget );
    GtkRequisition entry_req;
    entry_req.width = 2;
    entry_req.height = 2;
    ( * GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS( gcombo->entry ) )->size_request )
    ( gcombo->entry, &entry_req );
    GtkRequisition button_req;
    button_req.width = 2;
    button_req.height = 2;
    ( * GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS( gcombo->button ) )->size_request )
    ( gcombo->button, &button_req );
    requisition->width = w - button_req.width;
    requisition->height = entry_req.height;
  }
}

extern "C" {
  static gboolean
  gtk_window_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindow *win ) {
    DEBUG_MAIN_THREAD
    GtkPizza *pizza = GTK_PIZZA( widget );
    if( gdk_event->window != pizza->bin_window ) {
      return true;
    }
    win->GetUpdateRegion() = wxRegion( gdk_event->region );
    win->GtkSendPaintEvents();
    return FALSE;
  }
}

inline bool wxIsLowerChar( int code ) {
  return ( code >= 'a' && code <= 'z' );
}

inline bool wxIsUpperChar( int code ) {
  return ( code >= 'A' && code <= 'Z' );
}


#define TRACE_KEYS  _T("keyevent")

static long wxTranslateKeySymToWXKey( KeySym keysym, bool isChar ) {
  long key_code;
  switch( keysym ) {
    case GDK_Shift_L:
    case GDK_Shift_R:
      key_code = isChar ? 0 : WXK_SHIFT;
      break;
    case GDK_Control_L:
    case GDK_Control_R:
      key_code = isChar ? 0 : WXK_CONTROL;
      break;
    case GDK_Meta_L:
    case GDK_Meta_R:
    case GDK_Alt_L:
    case GDK_Alt_R:
    case GDK_Super_L:
    case GDK_Super_R:
      key_code = isChar ? 0 : WXK_ALT;
      break;
    case GDK_Scroll_Lock:
      key_code = isChar ? 0 : WXK_SCROLL;
      break;
    case GDK_Caps_Lock:
      key_code = isChar ? 0 : WXK_CAPITAL;
      break;
    case GDK_Num_Lock:
      key_code = isChar ? 0 : WXK_NUMLOCK;
      break;
    case GDK_Menu:
      key_code = WXK_MENU;
      break;
    case GDK_Help:
      key_code = WXK_HELP;
      break;
    case GDK_BackSpace:
      key_code = WXK_BACK;
      break;
    case GDK_ISO_Left_Tab:
    case GDK_Tab:
      key_code = WXK_TAB;
      break;
    case GDK_Linefeed:
    case GDK_Return:
      key_code = WXK_RETURN;
      break;
    case GDK_Clear:
      key_code = WXK_CLEAR;
      break;
    case GDK_Pause:
      key_code = WXK_PAUSE;
      break;
    case GDK_Select:
      key_code = WXK_SELECT;
      break;
    case GDK_Print:
      key_code = WXK_PRINT;
      break;
    case GDK_Execute:
      key_code = WXK_EXECUTE;
      break;
    case GDK_Escape:
      key_code = WXK_ESCAPE;
      break;
    case GDK_Delete:
      key_code = WXK_DELETE;
      break;
    case GDK_Home:
      key_code = WXK_HOME;
      break;
    case GDK_Left:
      key_code = WXK_LEFT;
      break;
    case GDK_Up:
      key_code = WXK_UP;
      break;
    case GDK_Right:
      key_code = WXK_RIGHT;
      break;
    case GDK_Down:
      key_code = WXK_DOWN;
      break;
    case GDK_Prior:
      key_code = WXK_PAGEUP;
      break;
    case GDK_Next:
      key_code = WXK_PAGEDOWN;
      break;
    case GDK_End:
      key_code = WXK_END;
      break;
    case GDK_Begin:
      key_code = WXK_HOME;
      break;
    case GDK_Insert:
      key_code = WXK_INSERT;
      break;
    case GDK_KP_0:
    case GDK_KP_1:
    case GDK_KP_2:
    case GDK_KP_3:
    case GDK_KP_4:
    case GDK_KP_5:
    case GDK_KP_6:
    case GDK_KP_7:
    case GDK_KP_8:
    case GDK_KP_9:
      key_code = ( isChar ? '0' : WXK_NUMPAD0 ) + keysym - GDK_KP_0;
      break;
    case GDK_KP_Space:
      key_code = isChar ? ' ' : WXK_NUMPAD_SPACE;
      break;
    case GDK_KP_Tab:
      key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB;
      break;
    case GDK_KP_Enter:
      key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER;
      break;
    case GDK_KP_F1:
      key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1;
      break;
    case GDK_KP_F2:
      key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2;
      break;
    case GDK_KP_F3:
      key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3;
      break;
    case GDK_KP_F4:
      key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4;
      break;
    case GDK_KP_Home:
      key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME;
      break;
    case GDK_KP_Left:
      key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT;
      break;
    case GDK_KP_Up:
      key_code = isChar ? WXK_UP : WXK_NUMPAD_UP;
      break;
    case GDK_KP_Right:
      key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT;
      break;
    case GDK_KP_Down:
      key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN;
      break;
    case GDK_KP_Prior:
      key_code = isChar ? WXK_PAGEUP : WXK_NUMPAD_PAGEUP;
      break;
    case GDK_KP_Next:
      key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
      break;
    case GDK_KP_End:
      key_code = isChar ? WXK_END : WXK_NUMPAD_END;
      break;
    case GDK_KP_Begin:
      key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN;
      break;
    case GDK_KP_Insert:
      key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT;
      break;
    case GDK_KP_Delete:
      key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE;
      break;
    case GDK_KP_Equal:
      key_code = isChar ? '=' : WXK_NUMPAD_EQUAL;
      break;
    case GDK_KP_Multiply:
      key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY;
      break;
    case GDK_KP_Add:
      key_code = isChar ? '+' : WXK_NUMPAD_ADD;
      break;
    case GDK_KP_Separator:
      key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR;
      break;
    case GDK_KP_Subtract:
      key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT;
      break;
    case GDK_KP_Decimal:
      key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL;
      break;
    case GDK_KP_Divide:
      key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE;
      break;
    case GDK_F1:
    case GDK_F2:
    case GDK_F3:
    case GDK_F4:
    case GDK_F5:
    case GDK_F6:
    case GDK_F7:
    case GDK_F8:
    case GDK_F9:
    case GDK_F10:
    case GDK_F11:
    case GDK_F12:
      key_code = WXK_F1 + keysym - GDK_F1;
      break;
    default:
      key_code = 0;
  }
  return key_code;
}

static inline bool wxIsAsciiKeysym( KeySym ks ) {
  return ks < 256;
}

static void wxFillOtherKeyEventFields( wxKeyEvent& event, wxWindow *win, GdkEventKey *gdk_event ) {
  int x = 0;
  int y = 0;
  GdkModifierType state;
  if( gdk_event->window ) {
    gdk_window_get_pointer( gdk_event->window, &x, &y, &state );
  }
  event.SetTimestamp( gdk_event->time );
  event.SetId( win->GetId() );
  event.m_shiftDown = ( gdk_event->state & GDK_SHIFT_MASK ) != 0;
  event.m_controlDown = ( gdk_event->state & GDK_CONTROL_MASK ) != 0;
  event.m_altDown = ( gdk_event->state & GDK_MOD1_MASK ) != 0;
  event.m_metaDown = ( gdk_event->state & GDK_META_MASK ) != 0;
  event.m_scanCode = gdk_event->keyval;
  event.m_rawCode = ( wxUint32 ) gdk_event->keyval;
  event.m_rawFlags = 0;
  event.m_uniChar = gdk_keyval_to_unicode( gdk_event->keyval );
  wxGetMousePosition( &x, &y );
  win->ScreenToClient( &x, &y );
  event.m_x = x;
  event.m_y = y;
  event.SetEventObject( win );
}

static bool wxTranslateGTKKeyEventToWx( wxKeyEvent& event, wxWindow *win, GdkEventKey *gdk_event ) {
  static struct {
    KeySym keysym;
    long   keycode;
  } s_lastKeyPress = { 0, 0 };
  KeySym keysym = gdk_event->keyval;
  wxLogTrace( TRACE_KEYS, _T( "Key %s event: keysym = %ld" ),
              event.GetEventType() == wxEVT_KEY_UP ? _T( "release" )
              : _T( "press" ),
              keysym );
  long key_code = wxTranslateKeySymToWXKey( keysym, false /* !isChar */ );
  if( !key_code ) {
    if( ( gdk_event->length == 1 ) || wxIsAsciiKeysym( keysym ) ) {
      if( !wxIsAsciiKeysym( keysym ) ) {
        keysym = ( KeySym )gdk_event->string[0];
      }
      Display *dpy = ( Display * )wxGetDisplay();
      KeyCode keycode = XKeysymToKeycode( dpy, keysym );
      wxLogTrace( TRACE_KEYS, _T( "\t-> keycode %d" ), keycode );
      KeySym keysymNormalized = XKeycodeToKeysym( dpy, keycode, 0 );
      key_code = keysymNormalized ? keysymNormalized : keysym;
      key_code = toupper( key_code );
    } else {
      key_code = 0;
      if( gdk_event->type == GDK_KEY_RELEASE ) {
        if( keysym == s_lastKeyPress.keysym ) {
          key_code = s_lastKeyPress.keycode;
        }
      }
    }
    if( gdk_event->type == GDK_KEY_PRESS ) {
      s_lastKeyPress.keysym = keysym;
      s_lastKeyPress.keycode = key_code;
    }
  }
  wxLogTrace( TRACE_KEYS, _T( "\t-> wxKeyCode %ld" ), key_code );
  if( !key_code ) {
    return false;
  }
  wxFillOtherKeyEventFields( event, win, gdk_event );
  event.m_keyCode = key_code;
  if( gdk_event->type == GDK_KEY_PRESS ||  gdk_event->type == GDK_KEY_RELEASE ) {
    event.m_uniChar = key_code;
  }
  return true;
}


struct wxGtkIMData {
  GtkIMContext *context;
  GdkEventKey  *lastKeyEvent;

  wxGtkIMData() {
    context = gtk_im_multicontext_new();
    lastKeyEvent = NULL;
  }
  ~wxGtkIMData() {
    g_object_unref( context );
  }
};

extern "C" {
  static gboolean
  gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) {
    DEBUG_MAIN_THREAD
    if( !win->m_hasVMT ) {
      return FALSE;
    }
    if( g_blockEventsOnDrag ) {
      return FALSE;
    }
    if( !GTK_WIDGET_HAS_FOCUS( widget ) ) {
      return FALSE;
    }
    wxKeyEvent event( wxEVT_KEY_DOWN );
    bool ret = false;
    bool return_after_IM = false;
    if( wxTranslateGTKKeyEventToWx( event, win, gdk_event ) ) {
      ret = win->GetEventHandler()->ProcessEvent( event );
    } else {
      return_after_IM = true;
    }
    if( ( !ret ) && ( win->m_imData != NULL ) && ( g_focusWindow == win ) ) {
      bool intercepted_by_IM = gtk_im_context_filter_keypress( win->m_imData->context, gdk_event );
      win->m_imData->lastKeyEvent = NULL;
      if( intercepted_by_IM ) {
        wxLogTrace( TRACE_KEYS, _T( "Key event intercepted by IM" ) );
        return TRUE;
      }
    }
    if( return_after_IM ) {
      return FALSE;
    }
    #if wxUSE_ACCEL
    if( !ret ) {
      wxWindow *ancestor = win;
      while( ancestor ) {
        int command = ancestor->GetAcceleratorTable()->GetCommand( event );
        if( command != -1 ) {
          wxCommandEvent menu_event( wxEVT_COMMAND_MENU_SELECTED, command );
          ret = ancestor->GetEventHandler()->ProcessEvent( menu_event );
          if( !ret ) {
            wxCommandEvent button_event( wxEVT_COMMAND_BUTTON_CLICKED, command );
            ret = ancestor->GetEventHandler()->ProcessEvent( button_event );
          }
          break;
        }
        if( ancestor->IsTopLevel() ) {
          break;
        }
        ancestor = ancestor->GetParent();
      }
    }
    #endif
    if( !ret ) {
      long key_code;
      KeySym keysym = gdk_event->keyval;
      key_code = wxTranslateKeySymToWXKey( keysym, true );
      if( !key_code ) {
        if( wxIsAsciiKeysym( keysym ) ) {
          key_code = ( unsigned char )keysym;
        } else if( gdk_event->length == 1 ) {
          key_code = ( unsigned char )gdk_event->string[0];
        }
      }
      if( key_code ) {
        wxLogTrace( TRACE_KEYS, _T( "Char event: %ld" ), key_code );
        event.m_keyCode = key_code;
        if( event.ControlDown() &&
            ( wxIsLowerChar( key_code ) || wxIsUpperChar( key_code ) ) ) {
          if( wxIsLowerChar( key_code ) ) {
            event.m_keyCode = key_code - 'a' + 1;
          }
          if( wxIsUpperChar( key_code ) ) {
            event.m_keyCode = key_code - 'A' + 1;
          }
          event.m_uniChar = event.m_keyCode;
        }
        wxWindow *parent = win;
        while( parent && !parent->IsTopLevel() ) {
          parent = parent->GetParent();
        }
        if( parent ) {
          event.SetEventType( wxEVT_CHAR_HOOK );
          ret = parent->GetEventHandler()->ProcessEvent( event );
        }
        if( !ret ) {
          event.SetEventType( wxEVT_CHAR );
          ret = win->GetEventHandler()->ProcessEvent( event );
        }
      }
    }
    if( !ret && ( gdk_event->keyval == GDK_Tab || gdk_event->keyval == GDK_ISO_Left_Tab )
      #if wxUSE_TEXTCTRL
        && !( win->HasFlag( wxTE_PROCESS_TAB ) && wxDynamicCast( win, wxTextCtrl ) )
      #endif
      ) {
      wxWindow * const parent = win->GetParent();
      if( parent && parent->HasFlag( wxTAB_TRAVERSAL ) ) {
        wxNavigationKeyEvent new_event;
        new_event.SetEventObject( parent );
        new_event.SetDirection( ( gdk_event->keyval == GDK_Tab ) );
        new_event.SetWindowChange( ( gdk_event->state & GDK_CONTROL_MASK ) );
        new_event.SetCurrentFocus( win );
        ret = parent->GetEventHandler()->ProcessEvent( new_event );
      }
    }
    return ret;
  }
}

extern "C" {
  static void
  gtk_wxwindow_commit_cb( GtkIMContext *context, const gchar  *str, wxWindow *window ) {
    wxKeyEvent event( wxEVT_KEY_DOWN );
    if( window->m_imData->lastKeyEvent ) {
      wxFillOtherKeyEventFields( event, window, window->m_imData->lastKeyEvent );
    } else {
      event.SetEventObject( window );
    }
    const wxWxCharBuffer data( wxGTK_CONV_BACK( str ) );
    if( !data ) {
      return;
    }
    bool ret = false;
    wxWindow *parent = window;
    while( parent && !parent->IsTopLevel() ) {
      parent = parent->GetParent();
    }
    for( const wxChar* pstr = data; *pstr; pstr++ ) {
      event.m_uniChar = *pstr;
      event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
      wxLogTrace( TRACE_KEYS, _T( "IM sent character '%c'" ), event.m_uniChar );
      if( event.ControlDown() &&
          ( wxIsLowerChar( *pstr ) || wxIsUpperChar( *pstr ) ) ) {
        if( wxIsLowerChar( *pstr ) ) {
          event.m_keyCode = *pstr - 'a' + 1;
        }
        if( wxIsUpperChar( *pstr ) ) {
          event.m_keyCode = *pstr - 'A' + 1;
        }
        event.m_keyCode = *pstr - 'a' + 1;
        event.m_uniChar = event.m_keyCode;
      }
      if( parent ) {
        event.SetEventType( wxEVT_CHAR_HOOK );
        ret = parent->GetEventHandler()->ProcessEvent( event );
      }
      if( !ret ) {
        event.SetEventType( wxEVT_CHAR );
        ret = window->GetEventHandler()->ProcessEvent( event );
      }
    }
  }
}

extern "C" {
  static gboolean
  gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) {
    DEBUG_MAIN_THREAD
    if( !win->m_hasVMT ) {
      return FALSE;
    }
    if( g_blockEventsOnDrag ) {
      return FALSE;
    }
    wxKeyEvent event( wxEVT_KEY_UP );
    if( !wxTranslateGTKKeyEventToWx( event, win, gdk_event ) ) {
      return FALSE;
    }
    return win->GTKProcessEvent( event );
  }
}

template<typename T> void InitMouseEvent( wxWindow *win, wxMouseEvent& event, T *gdk_event ) {
  event.SetTimestamp( gdk_event->time );
  event.m_shiftDown = ( gdk_event->state & GDK_SHIFT_MASK );
  event.m_controlDown = ( gdk_event->state & GDK_CONTROL_MASK );
  event.m_altDown = ( gdk_event->state & GDK_MOD1_MASK );
  event.m_metaDown = ( gdk_event->state & GDK_META_MASK );
  event.m_leftDown = ( gdk_event->state & GDK_BUTTON1_MASK );
  event.m_middleDown = ( gdk_event->state & GDK_BUTTON2_MASK );
  event.m_rightDown = ( gdk_event->state & GDK_BUTTON3_MASK );
  wxPoint pt = win->GetClientAreaOrigin();
  event.m_x = ( wxCoord )gdk_event->x - pt.x;
  event.m_y = ( wxCoord )gdk_event->y - pt.y;
  if( ( win->m_wxwindow ) && ( win->GetLayoutDirection() == wxLayout_RightToLeft ) ) {
    int window_width = gtk_pizza_get_rtl_offset( GTK_PIZZA( win->m_wxwindow ) );
    event.m_x = window_width - event.m_x;
  }
  event.SetEventObject( win );
  event.SetId( win->GetId() );
  event.SetTimestamp( gdk_event->time );
}

static void AdjustEventButtonState( wxMouseEvent& event ) {
  if( ( event.GetEventType() == wxEVT_LEFT_DOWN ) ||
      ( event.GetEventType() == wxEVT_LEFT_DCLICK ) ||
      ( event.GetEventType() == wxEVT_LEFT_UP ) ) {
    event.m_leftDown = !event.m_leftDown;
    return;
  }
  if( ( event.GetEventType() == wxEVT_MIDDLE_DOWN ) ||
      ( event.GetEventType() == wxEVT_MIDDLE_DCLICK ) ||
      ( event.GetEventType() == wxEVT_MIDDLE_UP ) ) {
    event.m_middleDown = !event.m_middleDown;
    return;
  }
  if( ( event.GetEventType() == wxEVT_RIGHT_DOWN ) ||
      ( event.GetEventType() == wxEVT_RIGHT_DCLICK ) ||
      ( event.GetEventType() == wxEVT_RIGHT_UP ) ) {
    event.m_rightDown = !event.m_rightDown;
    return;
  }
}

static wxWindow *FindWindowForMouseEvent( wxWindow *win, wxCoord& x, wxCoord& y ) {
  wxCoord xx = x;
  wxCoord yy = y;
  if( win->m_wxwindow ) {
    GtkPizza *pizza = GTK_PIZZA( win->m_wxwindow );
    xx += gtk_pizza_get_xoffset( pizza );
    yy += gtk_pizza_get_yoffset( pizza );
  }
  wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
  while( node ) {
    wxWindow *child = node->GetData();
    node = node->GetNext();
    if( !child->IsShown() ) {
      continue;
    }
    if( child->IsTransparentForMouse() ) {
      int xx1 = child->m_x;
      int yy1 = child->m_y;
      int xx2 = child->m_x + child->m_width;
      int yy2 = child->m_y + child->m_height;
      if( ( ( xx >= xx1 ) && ( xx <= xx1 + 10 ) && ( yy >= yy1 ) && ( yy <= yy2 ) ) ||
          ( ( xx >= xx2 - 10 ) && ( xx <= xx2 ) && ( yy >= yy1 ) && ( yy <= yy2 ) ) ||
          ( ( xx >= xx1 ) && ( xx <= xx2 ) && ( yy >= yy1 ) && ( yy <= yy1 + 10 ) ) ||
          ( ( xx >= xx1 ) && ( xx <= xx2 ) && ( yy >= yy2 - 1 ) && ( yy <= yy2 ) ) ) {
        win = child;
        x -= child->m_x;
        y -= child->m_y;
        break;
      }
    } else {
      if( ( child->m_wxwindow == ( GtkWidget* ) NULL ) &&
          ( child->m_x <= xx ) &&
          ( child->m_y <= yy ) &&
          ( child->m_x + child->m_width  >= xx ) &&
          ( child->m_y + child->m_height >= yy ) ) {
        win = child;
        x -= child->m_x;
        y -= child->m_y;
        break;
      }
    }
  }
  return win;
}

bool wxWindow::GTKProcessEvent( wxEvent& event ) const {
  return GetEventHandler()->ProcessEvent( event );
}

int wxWindow::GTKCallbackCommonPrologue( GdkEventAny *event ) const {
  DEBUG_MAIN_THREAD
  if( !m_hasVMT ) {
    return FALSE;
  }
  if( g_blockEventsOnDrag ) {
    return TRUE;
  }
  if( g_blockEventsOnScroll ) {
    return TRUE;
  }
  if( !GTKIsOwnWindow( event->window ) ) {
    return FALSE;
  }
  return -1;
}

#define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T)                                  \
  static int wxGtkCallbackCommonPrologue(T *event, wxWindow *win)        \
  {                                                                         \
    return win->GTKCallbackCommonPrologue((GdkEventAny *)event);          \
  }

wxDEFINE_COMMON_PROLOGUE_OVERLOAD( GdkEventButton )
wxDEFINE_COMMON_PROLOGUE_OVERLOAD( GdkEventMotion )
wxDEFINE_COMMON_PROLOGUE_OVERLOAD( GdkEventCrossing )

#undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD

#define wxCOMMON_CALLBACK_PROLOGUE(event, win)                                \
  const int rc = wxGtkCallbackCommonPrologue(event, win);                   \
  if ( rc != -1 )                                                           \
    return rc

static bool DoSendFocusEvents( wxWindow *win ) {
  wxChildFocusEvent eventChildFocus( win );
  ( void )win->GetEventHandler()->ProcessEvent( eventChildFocus );
  wxFocusEvent eventFocus( wxEVT_SET_FOCUS, win->GetId() );
  eventFocus.SetEventObject( win );
  return win->GetEventHandler()->ProcessEvent( eventFocus );
}

extern "C" {
  static gboolean
  gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindow *win ) {
    wxCOMMON_CALLBACK_PROLOGUE( gdk_event, win );
    g_lastButtonNumber = gdk_event->button;
    if( ( gdk_event->type == GDK_BUTTON_PRESS ) && ( win->m_wxwindow ) ) {
      GdkEvent *peek_event = gdk_event_peek();
      if( peek_event ) {
        if( ( peek_event->type == GDK_2BUTTON_PRESS ) ||
            ( peek_event->type == GDK_3BUTTON_PRESS ) ) {
          gdk_event_free( peek_event );
          return TRUE;
        } else
        { gdk_event_free( peek_event ); }
      }
    }
    wxEventType event_type = wxEVT_NULL;
    #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
    if( gdk_event->type == GDK_2BUTTON_PRESS &&
        !gtk_check_version( 2, 2, 0 ) &&
        gdk_event->button >= 1 && gdk_event->button <= 3 ) {
      GdkDisplay* display = gtk_widget_get_display( widget );
      display->button_click_time[1] = 0;
      display->button_click_time[0] = 0;
    }
    #endif
    if( gdk_event->button == 1 ) {
      switch( gdk_event->type ) {
        case GDK_3BUTTON_PRESS:
        case GDK_BUTTON_PRESS:
          event_type = wxEVT_LEFT_DOWN;
          break;
        case GDK_2BUTTON_PRESS:
          event_type = wxEVT_LEFT_DCLICK;
          break;
        default:
          ;
      }
    } else if( gdk_event->button == 2 ) {
      switch( gdk_event->type ) {
        case GDK_3BUTTON_PRESS:
        case GDK_BUTTON_PRESS:
          event_type = wxEVT_MIDDLE_DOWN;
          break;
        case GDK_2BUTTON_PRESS:
          event_type = wxEVT_MIDDLE_DCLICK;
          break;
        default:
          ;
      }
    } else if( gdk_event->button == 3 ) {
      switch( gdk_event->type ) {
        case GDK_3BUTTON_PRESS:
        case GDK_BUTTON_PRESS:
          event_type = wxEVT_RIGHT_DOWN;
          break;
        case GDK_2BUTTON_PRESS:
          event_type = wxEVT_RIGHT_DCLICK;
          break;
        default:
          ;
      }
    }
    if( event_type == wxEVT_NULL ) {
      return FALSE;
    }
    g_lastMouseEvent = ( GdkEvent* ) gdk_event;
    wxMouseEvent event( event_type );
    InitMouseEvent( win, event, gdk_event );
    AdjustEventButtonState( event );
    win->FixUpMouseEvent( widget, event.m_x, event.m_y );
    if( !g_captureWindow ) {
      win = FindWindowForMouseEvent( win, event.m_x, event.m_y );
    }
    event.SetEventObject( win );
    event.SetId( win->GetId() );
    bool ret = win->GTKProcessEvent( event );
    g_lastMouseEvent = NULL;
    if( ret ) {
      return TRUE;
    }
    if( ( event_type == wxEVT_LEFT_DOWN ) &&
        ( g_focusWindow != win ) && win->AcceptsFocus() ) {
      win->SetFocus();
    }
    if( event_type == wxEVT_RIGHT_DOWN ) {
      wxContextMenuEvent evtCtx(
        wxEVT_CONTEXT_MENU,
        win->GetId(),
        win->ClientToScreen( event.GetPosition() ) );
      evtCtx.SetEventObject( win );
      return win->GTKProcessEvent( evtCtx );
    }
    return FALSE;
  }
  static gboolean
  gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindow *win ) {
    wxCOMMON_CALLBACK_PROLOGUE( gdk_event, win );
    g_lastButtonNumber = 0;
    wxEventType event_type = wxEVT_NULL;
    switch( gdk_event->button ) {
      case 1:
        event_type = wxEVT_LEFT_UP;
        break;
      case 2:
        event_type = wxEVT_MIDDLE_UP;
        break;
      case 3:
        event_type = wxEVT_RIGHT_UP;
        break;
      default:
        return FALSE;
    }
    g_lastMouseEvent = ( GdkEvent* ) gdk_event;
    wxMouseEvent event( event_type );
    InitMouseEvent( win, event, gdk_event );
    AdjustEventButtonState( event );
    win->FixUpMouseEvent( widget, event.m_x, event.m_y );
    if( !g_captureWindow ) {
      win = FindWindowForMouseEvent( win, event.m_x, event.m_y );
    }
    event.SetEventObject( win );
    event.SetId( win->GetId() );
    bool ret = win->GTKProcessEvent( event );
    g_lastMouseEvent = NULL;
    return ret;
  }
  static gboolean
  gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxWindow *win ) {
    wxCOMMON_CALLBACK_PROLOGUE( gdk_event, win );
    if( gdk_event->is_hint ) {
      int x = 0;
      int y = 0;
      GdkModifierType state;
      gdk_window_get_pointer( gdk_event->window, &x, &y, &state );
      gdk_event->x = x;
      gdk_event->y = y;
    }
    g_lastMouseEvent = ( GdkEvent* ) gdk_event;
    wxMouseEvent event( wxEVT_MOTION );
    InitMouseEvent( win, event, gdk_event );
    if( g_captureWindow ) {
      GdkWindow *winUnderMouse = gdk_window_at_pointer( NULL, NULL );
      gdk_flush();
      bool hasMouse = winUnderMouse == gdk_event->window;
      if( hasMouse != g_captureWindowHasMouse ) {
        g_captureWindowHasMouse = hasMouse;
        wxMouseEvent eventM( g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
                             : wxEVT_LEAVE_WINDOW );
        InitMouseEvent( win, eventM, gdk_event );
        eventM.SetEventObject( win );
        win->GTKProcessEvent( eventM );
      }
    } else {
      win = FindWindowForMouseEvent( win, event.m_x, event.m_y );
      event.SetEventObject( win );
      event.SetId( win->GetId() );
    }
    if( !g_captureWindow ) {
      wxSetCursorEvent cevent( event.m_x, event.m_y );
      if( win->GTKProcessEvent( cevent ) ) {
        win->SetCursor( cevent.GetCursor() );
      }
    }
    bool ret = win->GTKProcessEvent( event );
    g_lastMouseEvent = NULL;
    return ret;
  }
  static gboolean
  window_scroll_event( GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win ) {
    DEBUG_MAIN_THREAD
    if( gdk_event->direction != GDK_SCROLL_UP &&
        gdk_event->direction != GDK_SCROLL_DOWN ) {
      return false;
    }
    wxMouseEvent event( wxEVT_MOUSEWHEEL );
    event.SetTimestamp( gdk_event->time );
    event.m_shiftDown = ( gdk_event->state & GDK_SHIFT_MASK );
    event.m_controlDown = ( gdk_event->state & GDK_CONTROL_MASK );
    event.m_altDown = ( gdk_event->state & GDK_MOD1_MASK );
    event.m_metaDown = ( gdk_event->state & GDK_META_MASK );
    event.m_leftDown = ( gdk_event->state & GDK_BUTTON1_MASK );
    event.m_middleDown = ( gdk_event->state & GDK_BUTTON2_MASK );
    event.m_rightDown = ( gdk_event->state & GDK_BUTTON3_MASK );
    event.m_linesPerAction = 3;
    event.m_wheelDelta = 120;
    if( gdk_event->direction == GDK_SCROLL_UP ) {
      event.m_wheelRotation = 120;
    } else
    { event.m_wheelRotation = -120; }
    wxPoint pt = win->GetClientAreaOrigin();
    event.m_x = ( wxCoord )gdk_event->x - pt.x;
    event.m_y = ( wxCoord )gdk_event->y - pt.y;
    event.SetEventObject( win );
    event.SetId( win->GetId() );
    event.SetTimestamp( gdk_event->time );
    return win->GTKProcessEvent( event );
  }
  static gboolean wxgtk_window_popup_menu_callback( GtkWidget*, wxWindow* win ) {
    wxContextMenuEvent event( wxEVT_CONTEXT_MENU, win->GetId(), wxPoint( -1, -1 ) );
    event.SetEventObject( win );
    return win->GTKProcessEvent( event );
  }
  static gboolean
  gtk_window_focus_in_callback( GtkWidget *widget, GdkEventFocus * ( event ), wxWindow *win ) {
    DEBUG_MAIN_THREAD
    if( win->m_imData ) {
      gtk_im_context_focus_in( win->m_imData->context );
    }
    g_focusWindowLast =
      g_focusWindow = win;
    g_focusWindowPending = NULL;
    wxLogTrace( TRACE_FOCUS,
                _T( "%s: focus in" ), win->GetName().c_str() );
    #if wxUSE_CARET
    wxCaret *caret = win->GetCaret();
    if( caret ) {
      caret->OnSetFocus();
    }
    #endif
    gboolean ret = FALSE;
    if( !win->m_hasFocus ) {
      win->m_hasFocus = true;
      ( void )DoSendFocusEvents( win );
      ret = TRUE;
    }
    if( win->m_wxwindow ) {
      return ret;
    }
    return FALSE;
  }
  static gboolean
  gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindow *win ) {
    DEBUG_MAIN_THREAD
    if( win->m_imData ) {
      gtk_im_context_focus_out( win->m_imData->context );
    }
    wxLogTrace( TRACE_FOCUS,
                _T( "%s: focus out" ), win->GetName().c_str() );
    wxWindow *winFocus = wxFindFocusedChild( win );
    if( winFocus ) {
      win = winFocus;
    }
    g_focusWindow = ( wxWindow * )NULL;
    #if wxUSE_CARET
    wxCaret *caret = win->GetCaret();
    if( caret ) {
      caret->OnKillFocus();
    }
    #endif
    if( win->m_hasFocus ) {
      const bool has_wxwindow = win->m_wxwindow != NULL;
      win->m_hasFocus = false;
      wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
      event.SetEventObject( win );
      ( void )win->GTKProcessEvent( event );
      if( has_wxwindow ) {
        return TRUE;
      }
    }
    return FALSE;
  }
  static gboolean
  gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindow *win ) {
    wxCOMMON_CALLBACK_PROLOGUE( gdk_event, win );
    if( gdk_event->mode != GDK_CROSSING_NORMAL ) {
      return FALSE;
    }
    int x = 0;
    int y = 0;
    GdkModifierType state = ( GdkModifierType )0;
    gdk_window_get_pointer( widget->window, &x, &y, &state );
    wxMouseEvent event( wxEVT_ENTER_WINDOW );
    InitMouseEvent( win, event, gdk_event );
    wxPoint pt = win->GetClientAreaOrigin();
    event.m_x = x + pt.x;
    event.m_y = y + pt.y;
    if( !g_captureWindow ) {
      wxSetCursorEvent cevent( event.m_x, event.m_y );
      if( win->GTKProcessEvent( cevent ) ) {
        win->SetCursor( cevent.GetCursor() );
      }
    }
    return win->GTKProcessEvent( event );
  }
  static gboolean gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindow *win ) {
    wxCOMMON_CALLBACK_PROLOGUE( gdk_event, win );
    if( gdk_event->mode != GDK_CROSSING_NORMAL ) {
      return FALSE;
    }
    wxMouseEvent event( wxEVT_LEAVE_WINDOW );
    event.SetTimestamp( gdk_event->time );
    event.SetEventObject( win );
    int x = 0;
    int y = 0;
    GdkModifierType state = ( GdkModifierType )0;
    gdk_window_get_pointer( widget->window, &x, &y, &state );
    event.m_shiftDown = ( state & GDK_SHIFT_MASK ) != 0;
    event.m_controlDown = ( state & GDK_CONTROL_MASK ) != 0;
    event.m_altDown = ( state & GDK_MOD1_MASK ) != 0;
    event.m_metaDown = ( state & GDK_META_MASK ) != 0;
    event.m_leftDown = ( state & GDK_BUTTON1_MASK ) != 0;
    event.m_middleDown = ( state & GDK_BUTTON2_MASK ) != 0;
    event.m_rightDown = ( state & GDK_BUTTON3_MASK ) != 0;
    wxPoint pt = win->GetClientAreaOrigin();
    event.m_x = x + pt.x;
    event.m_y = y + pt.y;
    return win->GTKProcessEvent( event );
  }
  static void
  gtk_scrollbar_value_changed( GtkRange* range, wxWindow* win ) {
    wxEventType eventType = win->GetScrollEventType( range );
    if( eventType != wxEVT_NULL ) {
      eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
      wxWindow::ScrollDir dir = win->ScrollDirFromRange( range );
      const int orient = wxWindow::OrientFromScrollDir( dir );
      wxScrollWinEvent event( eventType, win->GetScrollPos( orient ), orient );
      event.SetEventObject( win );
      win->GTKProcessEvent( event );
    }
  }
  static gboolean
  gtk_scrollbar_button_press_event( GtkRange*, GdkEventButton*, wxWindow* win ) {
    DEBUG_MAIN_THREAD
    g_blockEventsOnScroll = true;
    win->m_mouseButtonDown = true;
    return false;
  }
  static void
  gtk_scrollbar_event_after( GtkRange* range, GdkEvent* event, wxWindow* win ) {
    if( event->type == GDK_BUTTON_RELEASE ) {
      g_signal_handlers_block_by_func( range, ( void* )gtk_scrollbar_event_after, win );
      const int orient = wxWindow::OrientFromScrollDir(
                           win->ScrollDirFromRange( range ) );
      wxScrollWinEvent event( wxEVT_SCROLLWIN_THUMBRELEASE, win->GetScrollPos( orient ), orient );
      event.SetEventObject( win );
      win->GTKProcessEvent( event );
    }
  }
  static gboolean
  gtk_scrollbar_button_release_event( GtkRange* range, GdkEventButton*, wxWindow* win ) {
    DEBUG_MAIN_THREAD
    g_blockEventsOnScroll = false;
    win->m_mouseButtonDown = false;
    if( win->m_isScrolling ) {
      win->m_isScrolling = false;
      g_signal_handlers_unblock_by_func( range, ( void* )gtk_scrollbar_event_after, win );
    }
    return false;
  }
  static void
  gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) {
    DEBUG_MAIN_THREAD
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    if( win->m_imData ) {
      GtkPizza *pizza = GTK_PIZZA( m_widget );
      gtk_im_context_set_client_window( win->m_imData->context, pizza->bin_window );
    }
    wxWindowCreateEvent event( win );
    event.SetEventObject( win );
    win->GTKProcessEvent( event );
  }
  static
  void gtk_window_size_callback( GtkWidget * ( widget ), GtkAllocation *alloc, wxWindow *win ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    int client_width = 0;
    int client_height = 0;
    win->GetClientSize( &client_width, &client_height );
    if( ( client_width == win->m_oldClientWidth ) && ( client_height == win->m_oldClientHeight ) ) {
      return;
    }
    if( !client_width && !client_height ) {
      return;
    }
    win->m_oldClientWidth = client_width;
    win->m_oldClientHeight = client_height;
    if( !win->m_nativeSizeEvent ) {
      wxSizeEvent event( win->GetSize(), win->GetId() );
      event.SetEventObject( win );
      win->GTKProcessEvent( event );
    }
  }
  static
  void gtk_window_style_set_callback( GtkWidget *widget, GtkStyle *previous_style, wxWindow* win ) {
    if( win && previous_style ) {
      wxSysColourChangedEvent event;
      event.SetEventObject( win );
      win->GTKProcessEvent( event );
    }
  }

}

void wxConnectStyleSet( wxWindow* win ) {
  if( win->m_wxwindow )
  { g_signal_connect( win->m_wxwindow, "style_set", G_CALLBACK( gtk_window_style_set_callback ), win );
  }
}

void wxDisconnectStyleSet( wxWindow* win ) {
  if( win->m_wxwindow )
  { g_signal_handlers_disconnect_by_func( win->m_wxwindow, ( gpointer ) gtk_window_style_set_callback, win );
  }
}


class wxSuspendStyleEvents {
  public:
    wxSuspendStyleEvents( wxWindow* win ) {
      m_win = win;
      #if USE_STYLE_SET_CALLBACK
      if( win->IsTopLevel() ) {
        wxDisconnectStyleSet( win );
      }
      #endif
    }
    ~wxSuspendStyleEvents() {
      #if USE_STYLE_SET_CALLBACK
      if( m_win->IsTopLevel() ) {
        wxConnectStyleSet( m_win );
      }
      #endif
    }

    wxWindow* m_win;
};

wxWindow *wxWindowBase::DoFindFocus() {
  return ( wxWindow * )( g_focusWindowPending ? g_focusWindowPending : g_focusWindow );
}

static void wxInsertChildInWindow( wxWindow* parent, wxWindow* child ) {
  GtkPizza *pizza = GTK_PIZZA( parent->m_wxwindow );
  child->m_x += gtk_pizza_get_xoffset( pizza );
  child->m_y += gtk_pizza_get_yoffset( pizza );
  gtk_pizza_put( GTK_PIZZA( parent->m_wxwindow ), GTK_WIDGET( child->m_widget ), child->m_x,
                 child->m_y, child->m_width, child->m_height );
}

wxWindow *wxGetActiveWindow() {
  return wxWindow::FindFocus();
}

wxMouseState wxGetMouseState() {
  wxMouseState ms;
  gint x;
  gint y;
  GdkModifierType mask;
  gdk_window_get_pointer( NULL, &x, &y, &mask );
  ms.SetX( x );
  ms.SetY( y );
  ms.SetLeftDown( mask & GDK_BUTTON1_MASK );
  ms.SetMiddleDown( mask & GDK_BUTTON2_MASK );
  ms.SetRightDown( mask & GDK_BUTTON3_MASK );
  ms.SetControlDown( mask & GDK_CONTROL_MASK );
  ms.SetShiftDown( mask & GDK_SHIFT_MASK );
  ms.SetAltDown( mask & GDK_MOD1_MASK );
  ms.SetMetaDown( mask & GDK_META_MASK );
  return ms;
}

#ifdef __WXUNIVERSAL__
IMPLEMENT_ABSTRACT_CLASS( wxWindow, wxWindowBase )
#else
IMPLEMENT_DYNAMIC_CLASS( wxWindow, wxWindowBase )
#endif

void wxWindow::Init() {
  m_widget = ( GtkWidget * ) NULL;
  m_wxwindow = ( GtkWidget * ) NULL;
  m_focusWidget = ( GtkWidget * ) NULL;
  m_x = 0;
  m_y = 0;
  m_width = 0;
  m_height = 0;
  m_sizeSet = false;
  m_hasVMT = false;
  m_needParent = true;
  m_isBeingDeleted = false;
  m_showOnIdle = false;
  m_noExpose = false;
  m_nativeSizeEvent = false;
  m_hasScrolling = false;
  m_isScrolling = false;
  m_mouseButtonDown = false;
  m_blockScrollEvent = false;
  for( int dir = 0; dir < ScrollDir_Max; dir++ ) {
    m_scrollBar[dir] = NULL;
    m_scrollPos[dir] = 0;
    m_blockValueChanged[dir] = false;
  }
  m_oldClientWidth = m_oldClientHeight = 0;
  m_resizing = false;
  m_insertCallback = ( wxInsertChildFunction ) NULL;
  m_acceptsFocus = false;
  m_hasFocus = false;
  m_clipPaintRegion = false;
  m_needsStyleChange = false;
  m_cursor = *wxSTANDARD_CURSOR;
  m_imData = NULL;
  m_dirtyTabOrder = false;
}

wxWindow::wxWindow() {
  Init();
}

wxWindow::wxWindow( wxWindow *parent, wxWindowID id, const wxPoint &pos,
                    const wxSize &size, long style, const wxString &name ) {
  Init();
  Create( parent, id, pos, size, style, name );
}

bool wxWindow::Create( wxWindow *parent, wxWindowID id, const wxPoint &pos,
                       const wxSize &size, long style, const wxString &name ) {
  if( !PreCreation( parent, pos, size ) ||
      !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ) ) {
    wxFAIL_MSG( wxT( "wxWindow creation failed" ) );
    return false;
  }
  m_insertCallback = wxInsertChildInWindow;
  m_widget = gtk_scrolled_window_new( ( GtkAdjustment * ) NULL, ( GtkAdjustment * ) NULL );
  GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
  GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW( m_widget );
  GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS( m_widget ) );
  scroll_class->scrollbar_spacing = 0;
  if( HasFlag( wxALWAYS_SHOW_SB ) ) {
    gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
    scrolledWindow->hscrollbar_visible = TRUE;
    scrolledWindow->vscrollbar_visible = TRUE;
  } else
  { gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); }
  m_scrollBar[ScrollDir_Horz] = GTK_RANGE( scrolledWindow->hscrollbar );
  m_scrollBar[ScrollDir_Vert] = GTK_RANGE( scrolledWindow->vscrollbar );
  if( GetLayoutDirection() == wxLayout_RightToLeft ) {
    gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
  }
  m_wxwindow = gtk_pizza_new();
  if( HasFlag( wxSIMPLE_BORDER ) ) {
    gtk_container_set_border_width( ( GtkContainer* )m_wxwindow, 1 );
  } else if( HasFlag( wxRAISED_BORDER ) || HasFlag( wxSUNKEN_BORDER ) ) {
    gtk_container_set_border_width( ( GtkContainer* )m_wxwindow, 2 );
  }
  gtk_container_add( GTK_CONTAINER( m_widget ), m_wxwindow );
  GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
  m_acceptsFocus = true;
  for( int dir = 0; dir < ScrollDir_Max; dir++ ) {
    g_signal_connect( m_scrollBar[dir], "button_press_event", G_CALLBACK( gtk_scrollbar_button_press_event ), this );
    g_signal_connect( m_scrollBar[dir], "button_release_event", G_CALLBACK( gtk_scrollbar_button_release_event ), this );
    gulong handler_id = g_signal_connect( m_scrollBar[dir], "event_after",
                                          G_CALLBACK( gtk_scrollbar_event_after ), this );
    g_signal_handler_block( m_scrollBar[dir], handler_id );
    g_signal_connect_after( m_scrollBar[dir], "value_changed", G_CALLBACK( gtk_scrollbar_value_changed ), this );
  }
  gtk_widget_show( m_wxwindow );
  if( m_parent ) {
    m_parent->DoAddChild( this );
  }
  m_focusWidget = m_wxwindow;
  PostCreation();
  return true;
}

wxWindow::~wxWindow() {
  SendDestroyEvent();
  if( g_focusWindow == this ) {
    g_focusWindow = NULL;
  }
  if( g_focusWindowPending == this ) {
    g_focusWindowPending = NULL;
  }
  if( g_delayedFocus == this ) {
    g_delayedFocus = NULL;
  }
  m_isBeingDeleted = true;
  m_hasVMT = false;
  DestroyChildren();
  if( m_focusWidget != NULL ) {
    g_signal_handlers_disconnect_by_func( m_focusWidget, ( gpointer ) gtk_window_focus_in_callback, this );
    g_signal_handlers_disconnect_by_func( m_focusWidget, ( gpointer ) gtk_window_focus_out_callback, this );
  }
  if( m_widget ) {
    Show( false );
  }
  delete m_imData;
  if( m_wxwindow ) {
    gtk_widget_destroy( m_wxwindow );
    m_wxwindow = ( GtkWidget* ) NULL;
  }
  if( m_widget ) {
    gtk_widget_destroy( m_widget );
    m_widget = ( GtkWidget* ) NULL;
  }
}

bool wxWindow::PreCreation( wxWindow *parent, const wxPoint &pos,  const wxSize &size ) {
  wxCHECK_MSG( !m_needParent || parent, false, wxT( "Need complete parent." ) );
  m_width = WidthDefault( size.x ) ;
  m_height = HeightDefault( size.y );
  m_x = ( int )pos.x;
  m_y = ( int )pos.y;
  return true;
}

void wxWindow::PostCreation() {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( m_wxwindow ) {
    if( !m_noExpose ) {
      g_signal_connect( m_wxwindow, "expose_event", G_CALLBACK( gtk_window_expose_callback ), this );
      if( GetLayoutDirection() == wxLayout_LeftToRight ) {
        gtk_widget_set_redraw_on_allocate( GTK_WIDGET( m_wxwindow ), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
      }
    }
    m_imData = new wxGtkIMData;
    gtk_im_context_set_use_preedit( m_imData->context, FALSE );
    g_signal_connect( m_imData->context, "commit", G_CALLBACK( gtk_wxwindow_commit_cb ), this );
    g_signal_connect( m_widget, "expose_event", G_CALLBACK( gtk_window_own_expose_callback ), this );
  }
  if( !GTK_IS_WINDOW( m_widget ) ) {
    if( m_focusWidget == NULL ) {
      m_focusWidget = m_widget;
    }
    if( m_wxwindow ) {
      g_signal_connect( m_focusWidget, "focus_in_event", G_CALLBACK( gtk_window_focus_in_callback ), this );
      g_signal_connect( m_focusWidget, "focus_out_event", G_CALLBACK( gtk_window_focus_out_callback ), this );
    } else {
      g_signal_connect_after( m_focusWidget, "focus_in_event", G_CALLBACK( gtk_window_focus_in_callback ), this );
      g_signal_connect_after( m_focusWidget, "focus_out_event", G_CALLBACK( gtk_window_focus_out_callback ), this );
    }
  }
  GtkWidget *connect_widget = GetConnectWidget();
  ConnectWidget( connect_widget );
  g_signal_connect( connect_widget, "realize", G_CALLBACK( gtk_window_realized_callback ), this );
  if( m_wxwindow ) {
    g_signal_connect( m_wxwindow, "size_allocate", G_CALLBACK( gtk_window_size_callback ), this );
  }
  if( GTK_IS_COMBO( m_widget ) ) {
    GtkCombo *gcombo = GTK_COMBO( m_widget );
    g_signal_connect( gcombo->entry, "size_request", G_CALLBACK( wxgtk_combo_size_request_callback ), this );
  }
  #ifdef GTK_IS_FILE_CHOOSER_BUTTON
  else if( !gtk_check_version( 2, 6, 0 ) && GTK_IS_FILE_CHOOSER_BUTTON( m_widget ) ) {
  }
  #endif
  else {
    g_signal_connect( m_widget, "size_request",
                      G_CALLBACK( wxgtk_window_size_request_callback ),
                      this );
  }
  InheritAttributes();
  m_hasVMT = true;
  SetLayoutDirection( wxLayout_Default );
  if( IsShown() ) {
    gtk_widget_show( m_widget );
  }
}

void wxWindow::ConnectWidget( GtkWidget *widget ) {
  g_signal_connect( widget, "key_press_event",
                    G_CALLBACK( gtk_window_key_press_callback ), this );
  g_signal_connect( widget, "key_release_event",
                    G_CALLBACK( gtk_window_key_release_callback ), this );
  g_signal_connect( widget, "button_press_event",
                    G_CALLBACK( gtk_window_button_press_callback ), this );
  g_signal_connect( widget, "button_release_event",
                    G_CALLBACK( gtk_window_button_release_callback ), this );
  g_signal_connect( widget, "motion_notify_event",
                    G_CALLBACK( gtk_window_motion_notify_callback ), this );
  g_signal_connect( widget, "scroll_event",
                    G_CALLBACK( window_scroll_event ), this );
  g_signal_connect( widget, "popup_menu",
                    G_CALLBACK( wxgtk_window_popup_menu_callback ), this );
  g_signal_connect( widget, "enter_notify_event",
                    G_CALLBACK( gtk_window_enter_callback ), this );
  g_signal_connect( widget, "leave_notify_event",
                    G_CALLBACK( gtk_window_leave_callback ), this );
  #if USE_STYLE_SET_CALLBACK
  if( IsTopLevel() && m_wxwindow )
    g_signal_connect( m_wxwindow, "style_set",
                      G_CALLBACK( gtk_window_style_set_callback ), this );
  #endif
}

bool wxWindow::Destroy() {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid window" ) );
  m_hasVMT = false;
  return wxWindowBase::Destroy();
}

void wxWindow::DoMoveWindow( int x, int y, int width, int height ) {
  gtk_pizza_set_size( GTK_PIZZA( m_parent->m_wxwindow ), m_widget, x, y, width, height );
}

void wxWindow::DoSetSize( int x, int y, int width, int height, int sizeFlags ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid window" ) );
  wxASSERT_MSG( ( m_parent != NULL ), wxT( "wxWindow::SetSize requires parent.\n" ) );
  if( m_resizing ) {
    return;
  }
  m_resizing = true;
  int currentX, currentY;
  GetPosition( &currentX, &currentY );
  if( x == -1 && !( sizeFlags & wxSIZE_ALLOW_MINUS_ONE ) ) {
    x = currentX;
  }
  if( y == -1 && !( sizeFlags & wxSIZE_ALLOW_MINUS_ONE ) ) {
    y = currentY;
  }
  AdjustForParentClientOrigin( x, y, sizeFlags );
  if( ( ( sizeFlags & wxSIZE_AUTO_WIDTH ) && width == -1 ) ||
      ( ( sizeFlags & wxSIZE_AUTO_HEIGHT ) && height == -1 ) ) {
    const wxSize sizeBest = GetBestSize();
    if( ( sizeFlags & wxSIZE_AUTO_WIDTH ) && width == -1 ) {
      width = sizeBest.x;
    }
    if( ( sizeFlags & wxSIZE_AUTO_HEIGHT ) && height == -1 ) {
      height = sizeBest.y;
    }
  }
  if( width != -1 ) {
    m_width = width;
  }
  if( height != -1 ) {
    m_height = height;
  }
  int minWidth  = GetMinWidth(), minHeight = GetMinHeight(), maxWidth  = GetMaxWidth(), maxHeight = GetMaxHeight();
  if( ( minWidth  != -1 ) && ( m_width  < minWidth ) ) {
    m_width  = minWidth;
  }
  if( ( minHeight != -1 ) && ( m_height < minHeight ) ) {
    m_height = minHeight;
  }
  if( ( maxWidth  != -1 ) && ( m_width  > maxWidth ) ) {
    m_width  = maxWidth;
  }
  if( ( maxHeight != -1 ) && ( m_height > maxHeight ) ) {
    m_height = maxHeight;
  }
  #if wxUSE_TOOLBAR_NATIVE
  if( wxDynamicCast( GetParent(), wxToolBar ) ) {
    GtkWidget  *widget = GTK_WIDGET( m_widget );
    gtk_widget_set_size_request( widget, m_width, m_height );
  } else
  #endif
    if( m_parent->m_wxwindow == NULL ) {
      m_x = x;
      m_y = y;
      m_width = width;
      m_height = height;
    } else {
      GtkPizza *pizza = GTK_PIZZA( m_parent->m_wxwindow );
      if( ( sizeFlags & wxSIZE_ALLOW_MINUS_ONE ) == 0 ) {
        if( x != -1 ) {
          m_x = x + gtk_pizza_get_xoffset( pizza );
        }
        if( y != -1 ) {
          m_y = y + gtk_pizza_get_yoffset( pizza );
        }
      } else {
        m_x = x + gtk_pizza_get_xoffset( pizza );
        m_y = y + gtk_pizza_get_yoffset( pizza );
      }
      int left_border = 0;
      int right_border = 0;
      int top_border = 0;
      int bottom_border = 0;
      if( GTK_WIDGET_CAN_DEFAULT( m_widget ) ) {
        GtkBorder *default_border = NULL;
        gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
        if( default_border ) {
          left_border += default_border->left;
          right_border += default_border->right;
          top_border += default_border->top;
          bottom_border += default_border->bottom;
          gtk_border_free( default_border );
        }
      }
      DoMoveWindow( m_x - left_border, m_y - top_border, m_width + left_border + right_border,
                    m_height + top_border + bottom_border );
    }
  if( m_hasScrolling ) {
    GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
  }
  if( !m_nativeSizeEvent ) {
    wxSizeEvent event( wxSize( m_width, m_height ), GetId() );
    event.SetEventObject( this );
    GetEventHandler()->ProcessEvent( event );
  }
  m_resizing = false;
}

bool wxWindow::GtkShowFromOnIdle() {
  if( IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE( m_widget ) ) {
    GtkAllocation alloc;
    alloc.x = m_x;
    alloc.y = m_y;
    alloc.width = m_width;
    alloc.height = m_height;
    gtk_widget_size_allocate( m_widget, &alloc );
    gtk_widget_show( m_widget );
    wxShowEvent eventShow( GetId(), true );
    eventShow.SetEventObject( this );
    GetEventHandler()->ProcessEvent( eventShow );
    m_showOnIdle = false;
    return true;
  }
  return false;
}

void wxWindow::OnInternalIdle() {
  if( GtkShowFromOnIdle() ) {
    return;
  }
  if( m_dirtyTabOrder ) {
    m_dirtyTabOrder = false;
    RealizeTabOrder();
  }
  if( m_needsStyleChange ) {
    SetBackgroundStyle( GetBackgroundStyle() );
    m_needsStyleChange = false;
  }
  wxCursor cursor = m_cursor;
  if( g_globalCursor.Ok() ) {
    cursor = g_globalCursor;
  }
  if( cursor.Ok() ) {
    if( m_wxwindow ) {
      GdkWindow *window = GTK_PIZZA( m_wxwindow )->bin_window;
      if( window ) {
        gdk_window_set_cursor( window, cursor.GetCursor() );
      }
      if( !g_globalCursor.Ok() ) {
        cursor = *wxSTANDARD_CURSOR;
      }
      window = m_widget->window;
      if( ( window ) && !( GTK_WIDGET_NO_WINDOW( m_widget ) ) ) {
        gdk_window_set_cursor( window, cursor.GetCursor() );
      }
    } else if( m_widget ) {
      GdkWindow *window = m_widget->window;
      if( window && !GTK_WIDGET_NO_WINDOW( m_widget ) ) {
        gdk_window_set_cursor( window, cursor.GetCursor() );
      }
    }
  }
  if( wxUpdateUIEvent::CanUpdate( this ) && IsShownOnScreen() ) {
    UpdateWindowUI( wxUPDATE_UI_FROMIDLE );
  }
}

void wxWindow::DoGetSize( int *width, int *height ) const {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( width ) {
    ( *width ) = m_width;
  }
  if( height ) {
    ( *height ) = m_height;
  }
}

void wxWindow::DoSetClientSize( int width, int height ) {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( m_wxwindow ) {
    int dw = 0;
    int dh = 0;
    if( m_hasScrolling ) {
      GetScrollbarWidth( m_widget, dw, dh );
    }
    const int border = GTK_CONTAINER( m_wxwindow )->border_width;
    dw += 2 * border;
    dh += 2 * border;
    width += dw;
    height += dh;
  }
  SetSize( width, height );
}

void wxWindow::DoGetClientSize( int *width, int *height ) const {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  int w = m_width;
  int h = m_height;
  if( m_wxwindow ) {
    int dw = 0;
    int dh = 0;
    if( m_hasScrolling ) {
      GetScrollbarWidth( m_widget, dw, dh );
    }
    const int border = GTK_CONTAINER( m_wxwindow )->border_width;
    dw += 2 * border;
    dh += 2 * border;
    w -= dw;
    h -= dh;
    if( w < 0 ) {
      w = 0;
    }
    if( h < 0 ) {
      h = 0;
    }
  }
  if( width ) {
    *width = w;
  }
  if( height ) {
    *height = h;
  }
}

void wxWindow::DoGetPosition( int *x, int *y ) const {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  int dx = 0;
  int dy = 0;
  if( !IsTopLevel() && m_parent && m_parent->m_wxwindow ) {
    GtkPizza *pizza = GTK_PIZZA( m_parent->m_wxwindow );
    dx = gtk_pizza_get_xoffset( pizza );
    dy = gtk_pizza_get_yoffset( pizza );
  }
  if( m_x == -1 && m_y == -1 ) {
    GdkWindow *source = ( GdkWindow * ) NULL;
    if( m_wxwindow ) {
      source = GTK_PIZZA( m_wxwindow )->bin_window;
    } else {
      source = m_widget->window;
    }
    if( source ) {
      int org_x = 0;
      int org_y = 0;
      gdk_window_get_origin( source, &org_x, &org_y );
      if( GetParent() ) {
        GetParent()->ScreenToClient( &org_x, &org_y );
      }
      wx_const_cast( wxWindow*, this )->m_x = org_x;
      wx_const_cast( wxWindow*, this )->m_y = org_y;
    }
  }
  if( x ) {
    ( *x ) = m_x - dx;
  }
  if( y ) {
    ( *y ) = m_y - dy;
  }
}

void wxWindow::DoClientToScreen( int *x, int *y ) const {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( !m_widget->window ) {
    return;
  }
  GdkWindow *source = ( GdkWindow * ) NULL;
  if( m_wxwindow ) {
    source = GTK_PIZZA( m_wxwindow )->bin_window;
  } else {
    source = m_widget->window;
  }
  int org_x = 0;
  int org_y = 0;
  gdk_window_get_origin( source, &org_x, &org_y );
  if( !m_wxwindow ) {
    if( GTK_WIDGET_NO_WINDOW( m_widget ) ) {
      org_x += m_widget->allocation.x;
      org_y += m_widget->allocation.y;
    }
  }
  if( x ) {
    if( GetLayoutDirection() == wxLayout_RightToLeft ) {
      *x = ( GetClientSize().x - *x ) + org_x;
    } else
    { *x += org_x; }
  }
  if( y ) {
    *y += org_y;
  }
}

void wxWindow::DoScreenToClient( int *x, int *y ) const {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( !m_widget->window ) {
    return;
  }
  GdkWindow *source = ( GdkWindow * ) NULL;
  if( m_wxwindow ) {
    source = GTK_PIZZA( m_wxwindow )->bin_window;
  } else
  { source = m_widget->window; }
  int org_x = 0;
  int org_y = 0;
  gdk_window_get_origin( source, &org_x, &org_y );
  if( !m_wxwindow ) {
    if( GTK_WIDGET_NO_WINDOW( m_widget ) ) {
      org_x += m_widget->allocation.x;
      org_y += m_widget->allocation.y;
    }
  }
  if( x ) {
    if( GetLayoutDirection() == wxLayout_RightToLeft ) {
      *x = ( GetClientSize().x - *x ) - org_x;
    } else
    { *x -= org_x; }
  }
  if( y ) {
    *y -= org_y;
  }
}

bool wxWindow::Show( bool show ) {
  wxCHECK_MSG( ( m_widget != NULL ), false, wxT( "invalid window" ) );
  if( !wxWindowBase::Show( show ) ) {
    return false;
  }
  if( show ) {
    if( !m_showOnIdle ) {
      gtk_widget_show( m_widget );
      wxShowEvent eventShow( GetId(), show );
      eventShow.SetEventObject( this );
      GetEventHandler()->ProcessEvent( eventShow );
    }
  } else {
    gtk_widget_hide( m_widget );
    wxShowEvent eventShow( GetId(), show );
    eventShow.SetEventObject( this );
    GetEventHandler()->ProcessEvent( eventShow );
  }
  return true;
}

static void wxWindowNotifyEnable( wxWindow* win, bool enable ) {
  win->OnParentEnable( enable );
  for( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst(); node; node = node->GetNext() ) {
    wxWindow *child = node->GetData();
    if( !child->IsKindOf( CLASSINFO( wxDialog ) ) && !child->IsKindOf( CLASSINFO( wxFrame ) ) ) {
      wxWindowNotifyEnable( child, enable );
    }
  }
}

bool wxWindow::Enable( bool enable ) {
  wxCHECK_MSG( ( m_widget != NULL ), false, wxT( "invalid window" ) );
  if( !wxWindowBase::Enable( enable ) ) {
    return false;
  }
  gtk_widget_set_sensitive( m_widget, enable );
  if( m_wxwindow ) {
    gtk_widget_set_sensitive( m_wxwindow, enable );
  }
  wxWindowNotifyEnable( this, enable );
  return true;
}

int wxWindow::GetCharHeight() const {
  wxCHECK_MSG( ( m_widget != NULL ), 12, wxT( "invalid window" ) );
  wxFont font = GetFont();
  wxCHECK_MSG( font.Ok(), 12, wxT( "invalid font" ) );
  PangoContext *context = NULL;
  if( m_widget ) {
    context = gtk_widget_get_pango_context( m_widget );
  }
  if( !context ) {
    return 0;
  }
  PangoFontDescription *desc = font.GetNativeFontInfo()->description;
  PangoLayout *layout = pango_layout_new( context );
  pango_layout_set_font_description( layout, desc );
  pango_layout_set_text( layout, "H", 1 );
  PangoLayoutLine *line = ( PangoLayoutLine * )pango_layout_get_lines( layout )->data;
  PangoRectangle rect;
  pango_layout_line_get_extents( line, NULL, &rect );
  g_object_unref( layout );
  return ( int ) PANGO_PIXELS( rect.height );
}

int wxWindow::GetCharWidth() const {
  wxCHECK_MSG( ( m_widget != NULL ), 8, wxT( "invalid window" ) );
  wxFont font = GetFont();
  wxCHECK_MSG( font.Ok(), 8, wxT( "invalid font" ) );
  PangoContext *context = NULL;
  if( m_widget ) {
    context = gtk_widget_get_pango_context( m_widget );
  }
  if( !context ) {
    return 0;
  }
  PangoFontDescription *desc = font.GetNativeFontInfo()->description;
  PangoLayout *layout = pango_layout_new( context );
  pango_layout_set_font_description( layout, desc );
  pango_layout_set_text( layout, "g", 1 );
  PangoLayoutLine *line = ( PangoLayoutLine * )pango_layout_get_lines( layout )->data;
  PangoRectangle rect;
  pango_layout_line_get_extents( line, NULL, &rect );
  g_object_unref( layout );
  return ( int ) PANGO_PIXELS( rect.width );
}

void wxWindow::GetTextExtent( const wxString& string, int *x, int *y, int *descent,
                              int *externalLeading, const wxFont *theFont ) const {
  wxFont fontToUse = theFont ? *theFont : GetFont();
  wxCHECK_RET( fontToUse.Ok(), wxT( "invalid font" ) );
  if( string.empty() ) {
    if( x ) {
      ( *x ) = 0;
    }
    if( y ) {
      ( *y ) = 0;
    }
    return;
  }
  PangoContext *context = NULL;
  if( m_widget ) {
    context = gtk_widget_get_pango_context( m_widget );
  }
  if( !context ) {
    if( x ) {
      ( *x ) = 0;
    }
    if( y ) {
      ( *y ) = 0;
    }
    return;
  }
  PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
  PangoLayout *layout = pango_layout_new( context );
  pango_layout_set_font_description( layout, desc );
  {
    const wxCharBuffer data = wxGTK_CONV( string );
    if( data ) {
      pango_layout_set_text( layout, data, strlen( data ) );
    }
  }
  PangoRectangle rect;
  pango_layout_get_extents( layout, NULL, &rect );
  if( x ) {
    ( *x ) = ( wxCoord ) PANGO_PIXELS( rect.width );
  }
  if( y ) {
    ( *y ) = ( wxCoord ) PANGO_PIXELS( rect.height );
  }
  if( descent ) {
    PangoLayoutIter *iter = pango_layout_get_iter( layout );
    int baseline = pango_layout_iter_get_baseline( iter );
    pango_layout_iter_free( iter );
    *descent = *y - PANGO_PIXELS( baseline );
  }
  if( externalLeading ) {
    ( *externalLeading ) = 0;
  }
  g_object_unref( layout );
}

bool wxWindow::GTKSetDelayedFocusIfNeeded() {
  if( g_delayedFocus == this ) {
    if( GTK_WIDGET_REALIZED( m_widget ) ) {
      gtk_widget_grab_focus( m_widget );
      g_delayedFocus = NULL;
      return true;
    }
  }
  return false;
}

void wxWindow::SetFocus() {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
  if( m_hasFocus ) {
    return;
  }
  g_focusWindowPending = this;
  if( m_wxwindow ) {
    if( !GTK_WIDGET_HAS_FOCUS( m_wxwindow ) ) {
      gtk_widget_grab_focus( m_wxwindow );
    }
  } else if( m_widget ) {
    if( GTK_IS_CONTAINER( m_widget ) ) {
      #if wxUSE_RADIOBTN
      if( IsKindOf( CLASSINFO( wxRadioButton ) ) ) {
        gtk_widget_grab_focus( m_widget );
        return;
      }
      #endif
      gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
    } else if( GTK_WIDGET_CAN_FOCUS( m_widget ) && !GTK_WIDGET_HAS_FOCUS( m_widget ) ) {
      if( !GTK_WIDGET_REALIZED( m_widget ) ) {
        wxLogTrace( TRACE_FOCUS, _T( "Delaying setting focus to %s(%s)" ),
                    GetClassInfo()->GetClassName(), GetLabel().c_str() );
        g_delayedFocus = this;
      } else {
        wxLogTrace( TRACE_FOCUS, _T( "Setting focus to %s(%s)" ),
                    GetClassInfo()->GetClassName(), GetLabel().c_str() );
        gtk_widget_grab_focus( m_widget );
      }
    } else {
      wxLogTrace( TRACE_FOCUS, _T( "Can't set focus to %s(%s)" ),
                  GetClassInfo()->GetClassName(), GetLabel().c_str() );
    }
  }
}

bool wxWindow::AcceptsFocus() const {
  return m_acceptsFocus && wxWindowBase::AcceptsFocus();
}

bool wxWindow::Reparent( wxWindowBase *newParentBase ) {
  wxCHECK_MSG( ( m_widget != NULL ), false, wxT( "invalid window" ) );
  wxWindow *oldParent = m_parent,
            *newParent = ( wxWindow * )newParentBase;
  wxASSERT( GTK_IS_WIDGET( m_widget ) );
  if( !wxWindowBase::Reparent( newParent ) ) {
    return false;
  }
  wxASSERT( GTK_IS_WIDGET( m_widget ) );
  gtk_widget_ref( m_widget );
  if( oldParent ) {
    gtk_container_remove( GTK_CONTAINER( m_widget->parent ), m_widget );
  }
  wxASSERT( GTK_IS_WIDGET( m_widget ) );
  if( newParent ) {
    if( GTK_WIDGET_VISIBLE( newParent->m_widget ) ) {
      m_showOnIdle = true;
      gtk_widget_hide( m_widget );
    }
    ( *( newParent->m_insertCallback ) )( newParent, this );
  }
  gtk_widget_unref( m_widget );
  SetLayoutDirection( wxLayout_Default );
  return true;
}

void wxWindow::DoAddChild( wxWindow *child ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid window" ) );
  wxASSERT_MSG( ( child != NULL ), wxT( "invalid child window" ) );
  wxASSERT_MSG( ( m_insertCallback != NULL ), wxT( "invalid child insertion function" ) );
  AddChild( child );
  ( *m_insertCallback )( this, child );
}

void wxWindow::AddChild( wxWindowBase *child ) {
  wxWindowBase::AddChild( child );
  m_dirtyTabOrder = true;
  if( g_isIdle ) {
    wxapp_install_idle_handler();
  }
}

void wxWindow::RemoveChild( wxWindowBase *child ) {
  wxWindowBase::RemoveChild( child );
  m_dirtyTabOrder = true;
  if( g_isIdle ) {
    wxapp_install_idle_handler();
  }
}

wxLayoutDirection wxWindow::GTKGetLayout( GtkWidget *widget ) {
  return gtk_widget_get_direction( GTK_WIDGET( widget ) ) == GTK_TEXT_DIR_RTL
         ? wxLayout_RightToLeft : wxLayout_LeftToRight;
}

void wxWindow::GTKSetLayout( GtkWidget *widget, wxLayoutDirection dir ) {
  wxASSERT_MSG( dir != wxLayout_Default, _T( "invalid layout direction" ) );
  gtk_widget_set_direction( GTK_WIDGET( widget ), dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR );
}

wxLayoutDirection wxWindow::GetLayoutDirection() const {
  return GTKGetLayout( m_widget );
}

void wxWindow::SetLayoutDirection( wxLayoutDirection dir ) {
  if( dir == wxLayout_Default ) {
    const wxWindow *const parent = GetParent();
    if( parent ) {
      dir = parent->GetLayoutDirection();
    } else {
      dir = wxTheApp->GetLayoutDirection();
    }
  }
  if( dir == wxLayout_Default ) {
    return;
  }
  GTKSetLayout( m_widget, dir );
  if( m_wxwindow ) {
    GTKSetLayout( m_wxwindow, dir );
  }
}

wxCoord wxWindow::AdjustForLayoutDirection( wxCoord x, wxCoord( width ), wxCoord( widthTotal ) ) const {
  return x;
}

void wxWindow::DoMoveInTabOrder( wxWindow *win, MoveKind move ) {
  wxWindowBase::DoMoveInTabOrder( win, move );
  m_dirtyTabOrder = true;
  if( g_isIdle ) {
    wxapp_install_idle_handler();
  }
}

bool wxWindow::GTKWidgetNeedsMnemonic() const {
  return false;
}

void wxWindow::GTKWidgetDoSetMnemonic( GtkWidget * ( w ) ) {
}

void wxWindow::RealizeTabOrder() {
  if( m_wxwindow ) {
    if( !m_children.empty() ) {
      GList *chain = NULL;
      wxWindow* mnemonicWindow = NULL;
      for( wxWindowList::const_iterator i = m_children.begin();
           i != m_children.end();
           ++i ) {
        wxWindow *win = *i;
        if( mnemonicWindow ) {
          if( win->AcceptsFocusFromKeyboard() ) {
            GtkWidget* w = win->m_widget;
            if( !GTK_WIDGET_CAN_FOCUS( w ) ) {
              w = win->GetConnectWidget();
              if( !GTK_WIDGET_CAN_FOCUS( w ) )
              { w = NULL; }
            }
            if( w ) {
              mnemonicWindow->GTKWidgetDoSetMnemonic( w );
              mnemonicWindow = NULL;
            }
          }
        } else if( win->GTKWidgetNeedsMnemonic() ) {
          mnemonicWindow = win;
        }
        chain = g_list_prepend( chain, win->m_widget );
      }
      chain = g_list_reverse( chain );
      gtk_container_set_focus_chain( GTK_CONTAINER( m_wxwindow ), chain );
      g_list_free( chain );
    } else {
      gtk_container_unset_focus_chain( GTK_CONTAINER( m_wxwindow ) );
    }
  }
}

void wxWindow::Raise() {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( m_wxwindow && m_wxwindow->window ) {
    gdk_window_raise( m_wxwindow->window );
  } else if( m_widget->window ) {
    gdk_window_raise( m_widget->window );
  }
}

void wxWindow::Lower() {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( m_wxwindow && m_wxwindow->window ) {
    gdk_window_lower( m_wxwindow->window );
  } else if( m_widget->window ) {
    gdk_window_lower( m_widget->window );
  }
}

bool wxWindow::SetCursor( const wxCursor &cursor ) {
  if( !wxWindowBase::SetCursor( cursor.Ok() ? cursor : *wxSTANDARD_CURSOR ) ) {
    return false;
  }
  GTKUpdateCursor();
  return true;
}

void wxWindow::GTKUpdateCursor() {
  wxCursor cursor( g_globalCursor.Ok() ? g_globalCursor : GetCursor() );
  if( cursor.Ok() ) {
    wxArrayGdkWindows windowsThis;
    GdkWindow * const winThis = GTKGetWindow( windowsThis );
    if( winThis ) {
      gdk_window_set_cursor( winThis, cursor.GetCursor() );
    } else {
      const size_t count = windowsThis.size();
      for( size_t n = 0; n < count; n++ ) {
        GdkWindow *win = windowsThis[n];
        if( !win ) {
          wxFAIL_MSG( _T( "NULL window returned by GTKGetWindow()?" ) );
          continue;
        }
        gdk_window_set_cursor( win, cursor.GetCursor() );
      }
    }
  }
}

void wxWindow::WarpPointer( int x, int y ) {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  GdkWindow *window = ( GdkWindow* ) NULL;
  if( m_wxwindow ) {
    window = GTK_PIZZA( m_wxwindow )->bin_window;
  } else
  { window = GetConnectWidget()->window; }
  if( window ) {
    gdk_window_warp_pointer( window, x, y );
  }
}

wxWindow::ScrollDir wxWindow::ScrollDirFromRange( GtkRange *range ) const {
  for( int dir = 0; dir < ScrollDir_Max; dir++ ) {
    if( range == m_scrollBar[dir] ) {
      return ( ScrollDir )dir;
    }
  }
  wxFAIL_MSG( _T( "event from unknown scrollbar received" ) );
  return ScrollDir_Max;
}

bool wxWindow::DoScrollByUnits( ScrollDir dir, ScrollUnit unit, int units ) {
  bool changed = false;
  GtkRange* range = m_scrollBar[dir];
  if( range && units ) {
    GtkAdjustment* adj = range->adjustment;
    gdouble inc = unit == ScrollUnit_Line ? adj->step_increment : adj->page_increment;
    const int posOld = int( adj->value + 0.5 );
    gtk_range_set_value( range, posOld + units * inc );
    changed = int( adj->value + 0.5 ) != posOld;
  }
  return changed;
}

bool wxWindow::ScrollLines( int lines ) {
  return DoScrollByUnits( ScrollDir_Vert, ScrollUnit_Line, lines );
}

bool wxWindow::ScrollPages( int pages ) {
  return DoScrollByUnits( ScrollDir_Vert, ScrollUnit_Page, pages );
}

void wxWindow::Refresh( bool eraseBackground, const wxRect *rect ) {
  if( !m_widget ) {
    return;
  }
  if( !m_widget->window ) {
    return;
  }
  if( m_wxwindow ) {
    if( !GTK_PIZZA( m_wxwindow )->bin_window ) {
      return;
    }
    GdkRectangle gdk_rect,
                 *p;
    if( rect ) {
      gdk_rect.x = rect->x;
      gdk_rect.y = rect->y;
      gdk_rect.width = rect->width;
      gdk_rect.height = rect->height;
      if( GetLayoutDirection() == wxLayout_RightToLeft ) {
        gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
      }
      p = &gdk_rect;
    } else {
      p = NULL;
    }
    gdk_window_invalidate_rect( GTK_PIZZA( m_wxwindow )->bin_window, p, TRUE );
  }
}

void wxWindow::Update() {
  GtkUpdate();
  gdk_flush();
}

void wxWindow::GtkUpdate() {
  if( m_wxwindow && GTK_PIZZA( m_wxwindow )->bin_window ) {
    gdk_window_process_updates( GTK_PIZZA( m_wxwindow )->bin_window, FALSE );
  }
  if( m_widget && m_widget->window ) {
    gdk_window_process_updates( m_widget->window, FALSE );
  }
  for( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
       node;
       node = node->GetNext() ) {
    node->GetData()->GtkUpdate();
  }
}

bool wxWindow::DoIsExposed( int x, int y ) const {
  return m_updateRegion.Contains( x, y ) != wxOutRegion;
}


bool wxWindow::DoIsExposed( int x, int y, int w, int h ) const {
  if( GetLayoutDirection() == wxLayout_RightToLeft ) {
    return m_updateRegion.Contains( x - w, y, w, h ) != wxOutRegion;
  } else {
    return m_updateRegion.Contains( x, y, w, h ) != wxOutRegion;
  }
}

void wxWindow::GtkSendPaintEvents() {
  if( !m_wxwindow ) {
    m_updateRegion.Clear();
    return;
  }
  m_clipPaintRegion = true;
  m_nativeUpdateRegion = m_updateRegion;
  if( GetLayoutDirection() == wxLayout_RightToLeft ) {
    m_updateRegion.Clear();
    gint width;
    gdk_window_get_geometry( GTK_PIZZA( m_wxwindow )->bin_window,
                             NULL, NULL, &width, NULL, NULL );
    wxRegionIterator upd( m_nativeUpdateRegion );
    while( upd ) {
      wxRect rect;
      rect.x = upd.GetX();
      rect.y = upd.GetY();
      rect.width = upd.GetWidth();
      rect.height = upd.GetHeight();
      rect.x = width - rect.x - rect.width;
      m_updateRegion.Union( rect );
      ++upd;
    }
  }
  GtkPizza *pizza = GTK_PIZZA( m_wxwindow );
  if( GetThemeEnabled() && ( GetBackgroundStyle() == wxBG_STYLE_SYSTEM ) ) {
    wxWindow *parent = wxGetTopLevelParent( ( wxWindow * )this );
    if( !parent ) {
      parent = ( wxWindow* )this;
    }
    if( GTK_WIDGET_MAPPED( parent->m_widget ) ) {
      wxRegionIterator upd( m_nativeUpdateRegion );
      while( upd ) {
        GdkRectangle rect;
        rect.x = upd.GetX();
        rect.y = upd.GetY();
        rect.width = upd.GetWidth();
        rect.height = upd.GetHeight();
        gtk_paint_flat_box( parent->m_widget->style, pizza->bin_window, ( GtkStateType )GTK_WIDGET_STATE( m_wxwindow ),
                            GTK_SHADOW_NONE, &rect, parent->m_widget, ( char * )"base", 0, 0, -1, -1 );
        ++upd;
      }
    }
  } else {
    wxWindowDC dc( ( wxWindow* )this );
    dc.SetClippingRegion( m_updateRegion );
    if( GetBackgroundStyle() == wxBG_STYLE_COLOUR && GetBackgroundColour().Ok() &&
        wxSystemOptions::GetOptionInt( wxT( "gtk.window.force-background-colour" ) ) == 1 ) {
      dc.SetBackground( wxBrush( GetBackgroundColour() ) );
      dc.Clear();
    }
    wxEraseEvent erase_event( GetId(), &dc );
    erase_event.SetEventObject( this );
    GetEventHandler()->ProcessEvent( erase_event );
  }
  wxNcPaintEvent nc_paint_event( GetId() );
  nc_paint_event.SetEventObject( this );
  GetEventHandler()->ProcessEvent( nc_paint_event );
  wxPaintEvent paint_event( GetId() );
  paint_event.SetEventObject( this );
  GetEventHandler()->ProcessEvent( paint_event );
  m_clipPaintRegion = false;
  m_updateRegion.Clear();
  m_nativeUpdateRegion.Clear();
}

void wxWindow::SetDoubleBuffered( bool on ) {
  wxCHECK_RET( ( m_widget != NULL ), wxT( "invalid window" ) );
  if( m_wxwindow ) {
    gtk_widget_set_double_buffered( m_wxwindow, on );
  }
}

bool wxWindow::IsDoubleBuffered() const {
  return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
}

void wxWindow::ClearBackground() {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
}

#if wxUSE_TOOLTIPS
void wxWindow::DoSetToolTip( wxToolTip *tip ) {
  wxWindowBase::DoSetToolTip( tip );
  if( m_tooltip ) {
    m_tooltip->Apply( ( wxWindow * )this );
  } else {
    GtkWidget *w = GetConnectWidget();
    wxToolTip::Apply( w, wxCharBuffer() );
    #if GTK_CHECK_VERSION(2, 12, 0)
    if( gtk_check_version( 2, 12, 0 ) == NULL ) {
      gtk_widget_set_has_tooltip( w, FALSE );
    }
    #endif
  }
}

void wxWindow::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) {
  if( tip ) {
    wxString tmp( tip );
    gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV( tmp ), ( gchar* ) NULL );
  } else
  { gtk_tooltips_set_tip( tips, GetConnectWidget(), NULL, NULL ); }
}
#endif

bool wxWindow::SetBackgroundColour( const wxColour &colour ) {
  wxCHECK_MSG( m_widget != NULL, false, wxT( "invalid window" ) );
  if( !wxWindowBase::SetBackgroundColour( colour ) ) {
    return false;
  }
  if( colour.Ok() ) {
    m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
  }
  if( GetBackgroundStyle() != wxBG_STYLE_CUSTOM ) {
    ApplyWidgetStyle( true );
  }
  return true;
}

bool wxWindow::SetForegroundColour( const wxColour &colour ) {
  wxCHECK_MSG( m_widget != NULL, false, wxT( "invalid window" ) );
  if( !wxWindowBase::SetForegroundColour( colour ) ) {
    return false;
  }
  if( colour.Ok() ) {
    m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
  }
  ApplyWidgetStyle( true );
  return true;
}

PangoContext *wxWindow::GtkGetPangoDefaultContext() {
  return gtk_widget_get_pango_context( m_widget );
}

GtkRcStyle *wxWindow::CreateWidgetStyle( bool forceStyle ) {
  if( !forceStyle && !m_font.Ok() && !m_foregroundColour.Ok() && !m_backgroundColour.Ok() ) {
    return NULL;
  }
  GtkRcStyle *style = gtk_rc_style_new();
  if( m_font.Ok() ) {
    style->font_desc =
      pango_font_description_copy( m_font.GetNativeFontInfo()->description );
  }
  int flagsNormal = 0,
      flagsPrelight = 0,
      flagsActive = 0,
      flagsInsensitive = 0;
  if( m_foregroundColour.Ok() ) {
    const GdkColor *fg = m_foregroundColour.GetColor();
    style->fg[GTK_STATE_NORMAL] =
      style->text[GTK_STATE_NORMAL] = *fg;
    flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
    style->fg[GTK_STATE_PRELIGHT] =
      style->text[GTK_STATE_PRELIGHT] = *fg;
    flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
    style->fg[GTK_STATE_ACTIVE] =
      style->text[GTK_STATE_ACTIVE] = *fg;
    flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
  }
  if( m_backgroundColour.Ok() ) {
    const GdkColor *bg = m_backgroundColour.GetColor();
    style->bg[GTK_STATE_NORMAL] =
      style->base[GTK_STATE_NORMAL] = *bg;
    flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
    style->bg[GTK_STATE_PRELIGHT] =
      style->base[GTK_STATE_PRELIGHT] = *bg;
    flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
    style->bg[GTK_STATE_ACTIVE] =
      style->base[GTK_STATE_ACTIVE] = *bg;
    flagsActive |= GTK_RC_BG | GTK_RC_BASE;
    style->bg[GTK_STATE_INSENSITIVE] =
      style->base[GTK_STATE_INSENSITIVE] = *bg;
    flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
  }
  style->color_flags[GTK_STATE_NORMAL] = ( GtkRcFlags )flagsNormal;
  style->color_flags[GTK_STATE_PRELIGHT] = ( GtkRcFlags )flagsPrelight;
  style->color_flags[GTK_STATE_ACTIVE] = ( GtkRcFlags )flagsActive;
  style->color_flags[GTK_STATE_INSENSITIVE] = ( GtkRcFlags )flagsInsensitive;
  return style;
}

void wxWindow::ApplyWidgetStyle( bool forceStyle ) {
  GtkRcStyle *style = CreateWidgetStyle( forceStyle );
  if( style ) {
    DoApplyWidgetStyle( style );
    gtk_rc_style_unref( style );
  }
  InvalidateBestSize();
}

void wxWindow::DoApplyWidgetStyle( GtkRcStyle *style ) {
  wxSuspendStyleEvents s( ( wxWindow * )this );
  if( m_wxwindow ) {
    gtk_widget_modify_style( m_wxwindow, style );
  } else
  { gtk_widget_modify_style( m_widget, style ); }
}

bool wxWindow::SetBackgroundStyle( wxBackgroundStyle style ) {
  wxWindowBase::SetBackgroundStyle( style );
  if( style == wxBG_STYLE_CUSTOM ) {
    GdkWindow *window = ( GdkWindow* ) NULL;
    if( m_wxwindow ) {
      window = GTK_PIZZA( m_wxwindow )->bin_window;
    } else {
      window = GetConnectWidget()->window;
    }
    if( window ) {
      gdk_window_set_back_pixmap( window, None, False );
      #ifdef __X__
      Display* display = GDK_WINDOW_DISPLAY( window );
      XFlush( display );
      #endif
      m_needsStyleChange = false;
    } else {
      m_needsStyleChange = true;
    }
  } else {
    ApplyWidgetStyle( true );
  }
  return true;
}

#if wxUSE_DRAG_AND_DROP

void wxWindow::SetDropTarget( wxDropTarget *dropTarget ) {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
  GtkWidget *dnd_widget = GetConnectWidget();
  if( m_dropTarget ) {
    m_dropTarget->UnregisterWidget( dnd_widget );
  }
  if( m_dropTarget ) {
    delete m_dropTarget;
  }
  m_dropTarget = dropTarget;
  if( m_dropTarget ) {
    m_dropTarget->RegisterWidget( dnd_widget );
  }
}

#endif

GtkWidget* wxWindow::GetConnectWidget() {
  GtkWidget *connect_widget = m_widget;
  if( m_wxwindow ) {
    connect_widget = m_wxwindow;
  }
  return connect_widget;
}

bool wxWindow::GTKIsOwnWindow( GdkWindow *window ) const {
  wxArrayGdkWindows windowsThis;
  GdkWindow * const winThis = GTKGetWindow( windowsThis );
  return winThis ? window == winThis : windowsThis.Index( window ) != wxNOT_FOUND;
}

GdkWindow *wxWindow::GTKGetWindow( wxArrayGdkWindows & ( windows ) ) const {
  return m_wxwindow ? GTK_PIZZA( m_wxwindow )->bin_window : m_widget->window;
}

bool wxWindow::SetFont( const wxFont &font ) {
  wxCHECK_MSG( m_widget != NULL, false, wxT( "invalid window" ) );
  if( !wxWindowBase::SetFont( font ) ) {
    return false;
  }
  ApplyWidgetStyle( true );
  return true;
}

void wxWindow::DoCaptureMouse() {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
  GdkWindow *window = ( GdkWindow* ) NULL;
  if( m_wxwindow ) {
    window = GTK_PIZZA( m_wxwindow )->bin_window;
  } else
  { window = GetConnectWidget()->window; }
  wxCHECK_RET( window, _T( "CaptureMouse() failed" ) );
  const wxCursor* cursor = &m_cursor;
  if( !cursor->Ok() ) {
    cursor = wxSTANDARD_CURSOR;
  }
  gdk_pointer_grab( window, FALSE, ( GdkEventMask )( GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                    GDK_POINTER_MOTION_HINT_MASK | GDK_POINTER_MOTION_MASK ), ( GdkWindow * ) NULL,
                    cursor->GetCursor(), ( guint32 )GDK_CURRENT_TIME );
  g_captureWindow = this;
  g_captureWindowHasMouse = true;
}

void wxWindow::DoReleaseMouse() {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
  wxCHECK_RET( g_captureWindow, wxT( "can't release mouse - not captured" ) );
  g_captureWindow = ( wxWindow* ) NULL;
  GdkWindow *window = ( GdkWindow* ) NULL;
  if( m_wxwindow ) {
    window = GTK_PIZZA( m_wxwindow )->bin_window;
  } else
  { window = GetConnectWidget()->window; }
  if( !window ) {
    return;
  }
  gdk_pointer_ungrab( ( guint32 )GDK_CURRENT_TIME );
}

wxWindow *wxWindowBase::GetCapture() {
  return ( wxWindow * )g_captureWindow;
}

bool wxWindow::IsRetained() const {
  return false;
}

void wxWindow::BlockScrollEvent() {
  wxASSERT( !m_blockScrollEvent );
  m_blockScrollEvent = true;
}

void wxWindow::UnblockScrollEvent() {
  wxASSERT( m_blockScrollEvent );
  m_blockScrollEvent = false;
}

void wxWindow::SetScrollbar( int orient, int pos, int thumbVisible, int range, bool ( update ) ) {
  GtkRange * const sb = m_scrollBar[ScrollDirFromOrient( orient )];
  wxCHECK_RET( sb, _T( "this window is not scrollable" ) );
  if( range > 0 ) {
    m_hasScrolling = true;
  } else {
    range = thumbVisible = 1;
  }
  if( pos > range - thumbVisible ) {
    pos = range - thumbVisible;
  }
  if( pos < 0 ) {
    pos = 0;
  }
  GtkAdjustment * const adj = sb->adjustment;
  adj->step_increment = 1;
  adj->page_increment =
    adj->page_size = thumbVisible;
  adj->upper = range;
  SetScrollPos( orient, pos );
  gtk_adjustment_changed( adj );
}

void wxWindow::SetScrollPos( int orient, int pos, bool ( refresh ) ) {
  const int dir = ScrollDirFromOrient( orient );
  GtkRange * const sb = m_scrollBar[dir];
  wxCHECK_RET( sb, _T( "this window is not scrollable" ) );
  if( GetScrollPos( orient ) != pos ) {
    GtkAdjustment* adj = sb->adjustment;
    const int max = int( adj->upper - adj->page_size );
    if( pos > max ) {
      pos = max;
    }
    if( pos < 0 ) {
      pos = 0;
    }
    m_scrollPos[dir] = adj->value = pos;
    g_signal_handlers_disconnect_by_func( m_scrollBar[dir],
                                          ( gpointer )gtk_scrollbar_value_changed, this );
    gtk_adjustment_value_changed( adj );
    g_signal_connect_after( m_scrollBar[dir], "value_changed", G_CALLBACK( gtk_scrollbar_value_changed ), this );
  }
}

int wxWindow::GetScrollThumb( int orient ) const {
  GtkRange * const sb = m_scrollBar[ScrollDirFromOrient( orient )];
  wxCHECK_MSG( sb, 0, _T( "this window is not scrollable" ) );
  return int( sb->adjustment->page_size );
}

int wxWindow::GetScrollPos( int orient ) const {
  GtkRange * const sb = m_scrollBar[ScrollDirFromOrient( orient )];
  wxCHECK_MSG( sb, 0, _T( "this window is not scrollable" ) );
  return int( sb->adjustment->value + 0.5 );
}

int wxWindow::GetScrollRange( int orient ) const {
  GtkRange * const sb = m_scrollBar[ScrollDirFromOrient( orient )];
  wxCHECK_MSG( sb, 0, _T( "this window is not scrollable" ) );
  return int( sb->adjustment->upper );
}

static inline bool IsScrollIncrement( double increment, double x ) {
  wxASSERT( increment > 0 );
  const double tolerance = 1.0 / 1024;
  return fabs( increment - fabs( x ) ) < tolerance;
}

wxEventType wxWindow::GetScrollEventType( GtkRange* range ) {
  DEBUG_MAIN_THREAD
  if( g_isIdle ) {
    wxapp_install_idle_handler();
  }
  wxASSERT( range == m_scrollBar[0] || range == m_scrollBar[1] );
  const int barIndex = range == m_scrollBar[1];
  GtkAdjustment* adj = range->adjustment;
  const int value = int( adj->value + 0.5 );
  const double oldPos = m_scrollPos[barIndex];
  m_scrollPos[barIndex] = adj->value;
  if( !m_hasVMT || g_blockEventsOnDrag || value == int( oldPos + 0.5 ) ) {
    return wxEVT_NULL;
  }
  wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
  if( !m_isScrolling ) {
    const double diff = adj->value - oldPos;
    const bool isDown = diff > 0;
    if( IsScrollIncrement( adj->step_increment, diff ) ) {
      eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
    } else if( IsScrollIncrement( adj->page_increment, diff ) ) {
      eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
    } else if( m_mouseButtonDown ) {
      m_isScrolling = true;
    }
  }
  return eventType;
}

void wxWindow::ScrollWindow( int dx, int dy, const wxRect * ( rect ) ) {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid window" ) );
  wxCHECK_RET( m_wxwindow != NULL, wxT( "window needs client area for scrolling" ) );
  if( ( dx == 0 ) && ( dy == 0 ) ) {
    return;
  }
  m_clipPaintRegion = true;
  if( GetLayoutDirection() == wxLayout_RightToLeft ) {
    gtk_pizza_scroll( GTK_PIZZA( m_wxwindow ), dx, -dy );
  } else
  { gtk_pizza_scroll( GTK_PIZZA( m_wxwindow ), -dx, -dy ); }
  m_clipPaintRegion = false;
  bool restoreCaret = ( GetCaret() != NULL && GetCaret()->IsVisible() );
  if( restoreCaret ) {
    wxRect caretRect( GetCaret()->GetPosition(), GetCaret()->GetSize() );
    if( dx > 0 ) {
      caretRect.width += dx;
    } else {
      caretRect.x += dx;
      caretRect.width -= dx;
    }
    if( dy > 0 ) {
      caretRect.height += dy;
    } else {
      caretRect.y += dy;
      caretRect.height -= dy;
    }
    RefreshRect( caretRect );
  }
}

void wxWindow::GtkScrolledWindowSetBorder( GtkWidget* w, int wxstyle ) {
  if( !( wxstyle & wxNO_BORDER ) && !( wxstyle & wxBORDER_STATIC ) ) {
    GtkShadowType gtkstyle;
    if( wxstyle & wxBORDER_RAISED ) {
      gtkstyle = GTK_SHADOW_OUT;
    } else if( wxstyle & wxBORDER_SUNKEN ) {
      gtkstyle = GTK_SHADOW_IN;
    } else {
      gtkstyle = GTK_SHADOW_IN;
    }
    gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( w ),
                                         gtkstyle );
  }
}

void wxWindow::SetWindowStyleFlag( long style ) {
  wxWindowBase::SetWindowStyleFlag( style );
}

wxWindow* wxFindWindowAtPointer( wxPoint& pt ) {
  pt = wxGetMousePosition();
  wxWindow* found = wxFindWindowAtPoint( pt );
  return found;
}

wxPoint wxGetMousePosition() {
  int x, y;
  GdkWindow* windowAtPtr = gdk_window_at_pointer( & x, & y );
  Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY( windowAtPtr ) : GDK_DISPLAY();
  Window rootWindow = RootWindowOfScreen( DefaultScreenOfDisplay( display ) );
  Window rootReturn, childReturn;
  int rootX, rootY, winX, winY;
  unsigned int maskReturn;
  XQueryPointer( display,
                 rootWindow,
                 &rootReturn,
                 &childReturn,
                 &rootX, &rootY, &winX, &winY, &maskReturn );
  return wxPoint( rootX, rootY );
}


void wxAddGrab( wxWindow* window ) {
  gtk_grab_add( ( GtkWidget* ) window->GetHandle() );
}

void wxRemoveGrab( wxWindow* window ) {
  gtk_grab_remove( ( GtkWidget* ) window->GetHandle() );
}
